//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include "ChannelMagic.h"

#define MAGIC_NUMBER_HEADER 0x98
#define MAGIC_NUMBER_MAJOR 0x52
#define MAGIC_NUMBER_MINOR 0x23

void (*g_ReceiveRawData)(void*, int);

ChannelMagic::ChannelMagic()
{
    m_number = TYPE_SUCCEED;
}

ChannelMagic::ChannelMagic(unsigned char number):
    m_number(number)
{
}

ChannelMagic::~ChannelMagic()
{
}

void ChannelMagic::WriteBuffer(char* buffer)
{
    *buffer = (char) MAGIC_NUMBER_HEADER;
    *(buffer + 1) = (char) MAGIC_NUMBER_MAJOR;
    *(buffer + 2) = (char) MAGIC_NUMBER_MINOR;
    *(buffer + 3) = (char) m_number;
}

int ChannelMagic::Write(WriteOutputStream os)
{
    int size = 4; //GetPackageSize();
    char* buffer = new char[4];
    if (buffer == NULL) {
        return CHANNEL_OUT_OF_MEMORY;
    }
    WriteBuffer(buffer);
    int sent = 0;
    int ret = 0;
    do {
        int written = 0;
        ret = os((void *) (buffer + sent), size - sent, &written);
        if (0 != ret) {
            delete[] buffer;
            return CHANNEL_IO_ERROR;
        }
        sent += written;
    } while (sent != size);
    delete[] buffer;
    return CHANNEL_OK;
}

bool ChannelMagic::IsValidMagicNumber(char* header)
{
    return (header[0] == (char) MAGIC_NUMBER_HEADER
            && header[1] == (char) MAGIC_NUMBER_MAJOR
            && header[2] == (char) MAGIC_NUMBER_MINOR
            && (header[3] == (char) TYPE_SEND
                    || header[3] == (char) TYPE_SUCCEED
                    || header[3] == (char) TYPE_FAIL));
}

void ChannelMagic::DispatchRawData(void *data, int size)
{
    //Send data to the special receving obj;
    if (g_ReceiveRawData != NULL) {
        g_ReceiveRawData(data, size);
    }
}

int ChannelMagic::Read(ReadInputStream is)
{
    char header[4] = { 0, 0, 0, 0 };
    int ret = 0;
    int idx = 0;
    int recv = 0;

    do {
        ret = is(&header[3], 1, NULL);
        if (recv < 4) {
            recv++;
        }
        if (0 != ret) {
            // reading from physical layer failed.
            return CHANNEL_IO_ERROR; // io error
        }
        if (IsValidMagicNumber(header)) {
            m_number = (unsigned char) header[3];
            return CHANNEL_OK; // read ok
        }
        int count = 4;
        if (header[3] == (char) MAGIC_NUMBER_HEADER) {
            count = 3;
        }
        else if (header[2] == (char) MAGIC_NUMBER_HEADER
            && header[3] == (char) MAGIC_NUMBER_MAJOR) {
            count = 2;
        }
        else if (header[1] == (char) MAGIC_NUMBER_HEADER
            && header[2] == (char) MAGIC_NUMBER_MAJOR
            && header[3] == (char) MAGIC_NUMBER_MINOR) {
            count = 1;
        }
        if (idx == 0) {
            if (recv < 4) {
                if (count - (4 - recv) > 0) {
                    DispatchRawData(&header[4 - recv], count - (4 - recv));
                }
            }
            else {
                DispatchRawData(&header[0], count);
            }
            idx = count;
        }
        else if (idx < count) {
            DispatchRawData(&header[idx], count - idx);
            idx = count;
        }
        header[0] = header[1];
        header[1] = header[2];
        header[2] = header[3];
        header[3] = 0;
        idx--;
    } while (1);
}
